package com.guodi.map.controller;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.guodi.common.constant.StatusCode;
import com.guodi.common.utils.UuidUtil;
import com.guodi.core.boot.ctrl.BaseController;
import com.guodi.core.minio.util.MinioUtil;
import com.guodi.core.tool.api.R;
import com.guodi.core.tool.utils.Func;
import com.guodi.map.entity.MapMaterialFile;
import com.guodi.map.entity.MapMaterialType;
import com.guodi.map.service.IMapMaterialFileService;
import com.guodi.map.service.IMapMaterialTypeService;
import com.guodi.map.utils.RecursionUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import springfox.documentation.annotations.ApiIgnore;

import javax.servlet.http.HttpServletResponse;
import java.io.BufferedOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @描述：附件材料表
 * @作者: 戴伙进
 * @日期: 2020/6/3 9:06
 */
@RestController
@RequestMapping("/MapMaterialFile")
@Api(value = "API - MapMaterialFileController", tags = "地图附件材料表")
public class MapMaterialFileController extends BaseController {

    @Autowired
    public IMapMaterialFileService mapMaterialFileService;

    @Autowired
    public IMapMaterialTypeService mapMaterialTypeService;

    @PostMapping("/save")
    @ApiOperation(value = "新增")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "name", value = "目录/附件名称", dataType = "string", paramType = "query"),
            @ApiImplicitParam(name = "parentId", value = "上级目录id", required = true, dataType = "string", paramType = "query"),
            @ApiImplicitParam(name = "code", value = "目录编码", dataType = "string", paramType = "query"),
            @ApiImplicitParam(name = "sort", value = "排序", dataType = "int", paramType = "query"),
    })
    public R save(@ApiIgnore MapMaterialFile mapMaterialFile, MultipartFile file) throws Exception {
        if (StringUtils.isBlank(mapMaterialFile.getParentId())) {
            return R.fail(StatusCode.ERROR_CODE_40005, "传入的父节点不能为空");
        }
        prepareData(mapMaterialFile, true);
        mapMaterialFile.setId(UuidUtil.get32UUID());
        if (file != null) {
            //走的是上传材料文件的道路
            mapMaterialFile.setType(2);
            int index = file.getOriginalFilename().lastIndexOf(".");
            String fileType = file.getOriginalFilename().substring(index + 1).toLowerCase();
            String fileId = MinioUtil.uploadFile(file.getInputStream(), "map-material_file", file.getOriginalFilename());
            if(StringUtils.isBlank(fileId)){
                return R.fail(StatusCode.REQUEST_CODE_OPERATION_FAILURE,"数据上传失败！");
            }
            mapMaterialFile.setFileSize((double) file.getSize());
            mapMaterialFile.setFileFormat(fileType);
            mapMaterialFile.setPath(fileId);
            //获取上传原始文件名
            if (StringUtils.isBlank(mapMaterialFile.getName())) {
                String uploadName = file.getOriginalFilename().substring(0, index);
                mapMaterialFile.setName(uploadName);
            }
        } else {
            if (StringUtils.isBlank(mapMaterialFile.getName())) {
                return R.fail(StatusCode.ERROR_CODE_40005, "请传入目录名称");
            }
            LambdaQueryWrapper<MapMaterialFile> queryWrapper = new QueryWrapper().lambda();
            queryWrapper.eq(MapMaterialFile::getParentId, mapMaterialFile.getParentId());
            queryWrapper.eq(MapMaterialFile::getName, mapMaterialFile.getName());
            List<MapMaterialFile> materialFileList = mapMaterialFileService.list(queryWrapper);
            if (materialFileList.size() > 0) {
                return R.fail(StatusCode.ERROR_CODE_40005, "该目录名称已经存在");
            }
            //走的是创建目录的道路
            mapMaterialFile.setType(1);
        }
        //添加排序信息
        if (mapMaterialFile.getSort() == null) {
            Integer sortCount = mapMaterialFileService.selectMaxSort() + 1;
            mapMaterialFile.setSort(sortCount);
        }
        //保存信息
        boolean bol = mapMaterialFileService.save(mapMaterialFile);
        if (bol) {
            return R.data(mapMaterialFile);
        } else {
            return R.fail(StatusCode.REQUEST_CODE_OPERATION_FAILURE, "保存失败");
        }
    }

    @PostMapping("/delete")
    @ApiOperation(value = "根据id进行删除")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "id", value = "id", required = true,
                    dataType = "string", paramType = "query")
    })
    public R delete(String id) {
        if (StringUtils.isBlank(id)) {
            return R.fail(StatusCode.ERROR_CODE_40005, "请传入必要参数");
        }
        //需要递归删除
        List<MapMaterialFile> allChildrenMaterialFile = mapMaterialFileService.getAllChildrenById(id,2);
        for (int i = 0; i <allChildrenMaterialFile.size() ; i++) {
            deleteFileById(allChildrenMaterialFile.get(i).getId());
        }
        //首先删除子表数据
        mapMaterialFileService.deleteByIds(id);
        //删除主表数据
        return R.data(null);
    }

    /**
     * @描述:根据id批量删除
     * @入参:
     * @出参:
     * @作者: 戴伙进
     * @日期: 2020/6/10 16:39
    **/
    @PostMapping("/deleteByIds")
    @ApiOperation(value = "根据主键数组删除")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "ids", value = "传入的是一个JSON数组", required = true, allowMultiple = true, dataType = "string", paramType = "query")
    })
    public R deleteByIds(String ids) {
        JSONArray jsonArray = JSONObject.parseArray(ids);
        for (int i = 0; i < jsonArray.size(); i++) {
            String id = jsonArray.getString(i);
            deleteFileById(id);
            mapMaterialFileService.deleteByIds(id);
        }
        return R.data(null);
    }

    /**
     * @描述: 修改
     * @入参:
     * @出参:
     * @作者: 戴伙进
     * @日期: 2020-06-03
     **/
    @PostMapping("/update")
    @ApiOperation(value = "修改")
    public R update(MapMaterialFile mapMaterialFile) {
        if (StringUtils.isBlank(mapMaterialFile.getId())) {
            return R.fail(StatusCode.ERROR_CODE_40005, "请传入必要id");
        }
        LambdaQueryWrapper<MapMaterialFile> queryWrapper = new QueryWrapper().lambda();
        queryWrapper.eq(MapMaterialFile::getParentId, mapMaterialFile.getParentId());
        queryWrapper.eq(MapMaterialFile::getName, mapMaterialFile.getName());
        queryWrapper.ne(MapMaterialFile::getId, mapMaterialFile.getId());
        List<MapMaterialFile> materialFileList = mapMaterialFileService.list(queryWrapper);
        if (materialFileList.size() > 0) {
            return R.fail(StatusCode.ERROR_CODE_40005, "该目录名称已经存在");
        }
        prepareData(mapMaterialFile, false);
        mapMaterialFileService.updateById(mapMaterialFile);
        return R.data(null);
    }

    /**
     * @描述: 根据实体属性分页查询
     * @入参:
     * @出参:
     * @作者: 戴伙进
     * @日期: 2020-06-03
     **/
    @PostMapping("/listByEntity")
    @ApiOperation(value = " 根据实体属性分页查询")
    public R listByEntity(Page page) {
        LambdaQueryWrapper<MapMaterialFile> wrapper = new QueryWrapper().lambda();
        IPage<MapMaterialFile> iPage = mapMaterialFileService.page(page, wrapper);
        return R.data(iPage);
    }

    /**
     * @描述: 根据主键查询单条记录
     * @入参:
     * @出参:
     * @作者: 戴伙进
     * @日期: 2020-06-03
     **/
    @PostMapping("/findById")
    @ApiOperation(value = "根据主键查询")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "id", value = "ID", required = true,
                    dataType = "string", paramType = "query")
    })
    public R findById(String id) {
        if (StringUtils.isBlank(id)) {
            return R.fail(StatusCode.ERROR_CODE_40005, "id不能为空");
        }
        MapMaterialFile mapMaterialFile = mapMaterialFileService.getById(id);
        return R.data(mapMaterialFile);
    }

    /**
     * @描述:递归获取查询的文件目录树
     * @入参:
     * @出参:
     * @作者: 戴伙进
     * @日期: 2020/6/4 15:17
     **/
    @PostMapping("/listMaterialTreeByParentId")
    @ApiOperation(value = "递归获取材料文件")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "parentId", value = "父节点", required = true,
                    dataType = "string", paramType = "query"),
            @ApiImplicitParam(name = "type", value = "文件类型，1是目录，2是文件",
                    dataType = "string", paramType = "query")
    })
    public R listMaterialTreeByParentId(String parentId, String type) {
        if (StringUtils.isBlank(parentId)) {
            return R.fail(StatusCode.ERROR_CODE_40005, "请传入父节点");
        }
        //获取行政区划树
        List<MapMaterialFile> treeList = mapMaterialFileService.listMaterialTreeByParentId(parentId, type);
        return R.data(treeList);
    }

    @PostMapping("/listMaterialTreeByCode")
    @ApiOperation(value = "通过编码递归获取材料文件")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "code", value = "材料编码", required = true,
                    dataType = "string", paramType = "query"),
            @ApiImplicitParam(name = "type", value = "文件类型，1是目录，2是文件",
                    dataType = "string", paramType = "query")
    })
    public R listMaterialTreeByCode(String code, String type) {
        if (StringUtils.isBlank(code)) {
            return R.fail(StatusCode.ERROR_CODE_40005, "请传入编码");
        }
        //通过code获取文件材料类型id
        LambdaQueryWrapper<MapMaterialType> queryWrapper = new QueryWrapper().lambda();
        queryWrapper.eq(MapMaterialType::getCode, code);
        List<MapMaterialType> mapMaterialTypeList =mapMaterialTypeService.list(queryWrapper);
        if(mapMaterialTypeList.size() >0){
            MapMaterialType mapMaterialType = mapMaterialTypeList.get(0);
            String id = mapMaterialType.getId();
            //获取树数据
            List<MapMaterialFile> treeList = mapMaterialFileService.listMaterialTreeByParentId(id, type);
            return R.data(treeList);
        }else {
            return R.fail(StatusCode.ERROR_CODE_40005, "查无该编码数据");
        }
    }

    /**
     * @描述:获取所有的递归树的列表
     * @入参:
     * @出参:
     * @作者: 戴伙进
     * @日期: 2020/6/4 15:24
     **/
    @PostMapping("/getMaterialTreeList")
    @ApiOperation(value = "通过父节点获取列表数据")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "parentId", value = "父节点", required = true,
                    dataType = "string", paramType = "query"),
            @ApiImplicitParam(name = "keyWord", value = "关键字", dataType = "string", paramType = "query")
    })
    public R getMaterialTreeList(String parentId, String keyWord) {
        if (StringUtils.isBlank(parentId)) {
            return R.fail(StatusCode.ERROR_CODE_40005, "请传入父节点");
        }
        List<String> targetIDs = new ArrayList<>();
        List<MapMaterialFile> targetList = new ArrayList<>();
        //获取父节点下的所有数据
        List<MapMaterialFile> treeList = mapMaterialFileService.getAllByParentId(parentId, null, null);
        if(StringUtils.isNotBlank(keyWord)){
            // 检索可能存在的目录名称或者文件名称
            List<String> ids = treeList.stream().filter(x -> x.getName().indexOf(keyWord) != -1).map(x -> x.getId()).collect(Collectors.toList());
            if(ids.size() > 0){
                targetIDs.addAll(ids);
            }
        }
        // 获取所有目标ID后，再次查询数据库数据
        if(targetIDs.size() > 0){
            // 去重后查询检索数据构造结果树
            List<String> childrenIds = targetIDs.stream().distinct().collect(Collectors.toList());
            List<MapMaterialFile> tempList = mapMaterialFileService.getAllParentById(childrenIds);
            // 去重，防止出现重复数据
            Map<Object, Boolean> map = new HashMap<>();
            targetList = tempList.stream().filter(i -> map.putIfAbsent(i.getId(), Boolean.TRUE) == null).collect(Collectors.toList());

        }else {
            if(StringUtils.isNotBlank(keyWord)){
                targetList = null;
            }else {
                targetList = treeList;
            }
        }
        if(null != targetList && targetList.size() > 0){
            //2020-0627兼容pgSQL
            boolean isTrue = targetList.stream().filter(m -> m.getTreeLevel() == 0).findAny().isPresent();
            if(isTrue){
                List<MapMaterialFile> resultList = new ArrayList<>();
                //进行排序
                sortList(targetList, parentId, resultList, 1);
                return R.data(resultList);
            }else {
                return R.data(targetList);
            }
        }
    return R.status(true);
    }

    /**
     * @描述:通过编码获取所有的递归树的列表
     * @入参:
     * @出参:
     * @作者: 戴伙进
     * @日期: 2020/6/4 15:24
     **/
    @PostMapping("/getMaterialTreeListByCode")
    @ApiOperation(value = "通过编码获取列表数据")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "code", value = "编码", required = true,
                    dataType = "string", paramType = "query"),
            @ApiImplicitParam(name = "keyWord", value = "关键字", dataType = "string", paramType = "query")
    })
    public R getMaterialTreeListByCode(String code, String keyWord) {
        if (StringUtils.isBlank(code)) {
            return R.fail(StatusCode.ERROR_CODE_40005, "请传入才材料编码");
        }
        //通过code获取文件材料类型id
        LambdaQueryWrapper<MapMaterialType> queryWrapper = new QueryWrapper().lambda();
        queryWrapper.eq(MapMaterialType::getCode, code);
        List<MapMaterialType> mapMaterialTypeList =mapMaterialTypeService.list(queryWrapper);
        if(mapMaterialTypeList.size() >0){
            MapMaterialType mapMaterialType = mapMaterialTypeList.get(0);
            String parentId = mapMaterialType.getId();
            List<String> targetIDs = new ArrayList<>();
            List<MapMaterialFile> targetList = new ArrayList<>();
            //获取父节点下的所有数据
            List<MapMaterialFile> treeList = mapMaterialFileService.getAllByParentId(parentId, null, null);
            if(StringUtils.isNotBlank(keyWord)){
                // 检索可能存在的目录名称或者文件名称
                List<String> ids = treeList.stream().filter(x -> x.getName().indexOf(keyWord) != -1).map(x -> x.getId()).collect(Collectors.toList());
                if(ids.size() > 0){
                    targetIDs.addAll(ids);
                }
            }
            // 获取所有目标ID后，再次查询数据库数据
            if(targetIDs.size() > 0){
                // 去重后查询检索数据构造结果树
                List<String> childrenIds = targetIDs.stream().distinct().collect(Collectors.toList());
                List<MapMaterialFile> tempList = mapMaterialFileService.getAllParentById(childrenIds);
                // 去重，防止出现重复数据
                Map<Object, Boolean> map = new HashMap<>();
                targetList = tempList.stream().filter(i -> map.putIfAbsent(i.getId(), Boolean.TRUE) == null).collect(Collectors.toList());
            }else {
                if(StringUtils.isNotBlank(keyWord)){
                    targetList = null;
                }else {
                    targetList = treeList;
                }
            }
            if(null != targetList && targetList.size() > 0){
                //2020-0627兼容pgSQL
                boolean isTrue = targetList.stream().filter(m -> m.getTreeLevel() == 0).findAny().isPresent();
                if(isTrue){
                    List<MapMaterialFile> resultList = new ArrayList<>();
                    sortList(targetList, parentId, resultList, 1);
                    return R.data(RecursionUtil.buildMapMaterialFileTree(resultList, parentId));
                }else {
                    return R.data(RecursionUtil.buildMapMaterialFileTree(targetList, parentId));
                }
            }
            return R.status(true);
        }else {
            return R.fail(StatusCode.ERROR_CODE_40005, "查无该编码数据");
        }
    }

    @PostMapping("/downloadCompressionFileToStream")
    @ApiOperation(value = "下载目录节点下的所有文件,响应的是文件流的方式")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "id", value = "节点id", required = true,
                    dataType = "string", paramType = "query")
    })
    public void downloadCompressionFileToStream(String id, HttpServletResponse response) {
        MapMaterialFile mainMapMaterialFile = mapMaterialFileService.getById(id);
        //获取父节点下的所有数据
        List<MapMaterialFile> treeList = mapMaterialFileService.getAllChildrenById(id,null);
        List<String> fileUrlList = new ArrayList<>();
        for (MapMaterialFile node : treeList) {
            if (node.getType() == 2) {
                fileUrlList.add(node.getPath());
            }
        }
        if(fileUrlList.size() > 0){
            MinioUtil.batchDownload(fileUrlList, mainMapMaterialFile.getName(), response);
        }
    }

    @PostMapping("/downloadBatchFileToStream")
    @ApiOperation(value = "下载选中的批量文件,文件流的方式输出")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "ids", value = "传入的是一个文件id的Json数组", required = true,
                    dataType = "string", paramType = "query")
    })
    public void downloadBatchFileToStream(String ids, HttpServletResponse response) {
        JSONArray idArray = JSONObject.parseArray(ids);
        if (idArray.size() == 0) {
            return;
        }
        //获取其中的一个
        String firstId = (String) idArray.get(0);
        List<MapMaterialFile> findAllParent = mapMaterialFileService.getAllParentById(Func.toStrList(firstId));
        String downloadName = findAllParent.get(findAllParent.size() - 1).getName();

        List<String> fileUrlList = new ArrayList<>();
        for (int i = 0; i < idArray.size(); i++) {
            MapMaterialFile mapMaterialFile = mapMaterialFileService.getById(idArray.getString(i));
            if (mapMaterialFile.getType() == 2 && StringUtils.isNotBlank(mapMaterialFile.getPath())) {
                fileUrlList.add(mapMaterialFile.getPath());
            }
        }
        if (fileUrlList.size() > 0) {
            MinioUtil.batchDownload(fileUrlList, downloadName, response);
        }
    }

    /**
     * @描述:下载文件
     * @入参:
     * @出参:
     * @作者: 戴伙进
     * @日期: 2020/6/6 17:14
     **/
    @PostMapping("/downloadFile")
    @ApiOperation(value = "下载文件")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "fileName", value = "当前文件名称", required = true, dataType = "string", paramType = "query"),
            @ApiImplicitParam(name = "filePath", value = "路径", required = true, dataType = "string", paramType = "query")
    })
    public void downloadFile(String fileName, String filePath, HttpServletResponse response){
        InputStream inputStream = MinioUtil.download(filePath);
        int len = 0;
        byte [] buff=new byte[1024];
        try{
            OutputStream out = new BufferedOutputStream(response.getOutputStream());
            response.setCharacterEncoding("UTF-8");
            response.setHeader("content-Type", "application/octet-stream");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
            while((len=inputStream.read(buff))!=-1){
                out.write(buff, 0, len);
            }
            out.flush();
            out.close();
            inputStream.close();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * @描述:根据id删除FDFS文件上的文件
     * @入参:
     * @出参:
     * @作者: 戴伙进
     * @日期: 2020/6/5 15:25
     **/
    public void deleteFileById(String id) {
        MapMaterialFile mapMaterialFile = mapMaterialFileService.getById(id);
        if (mapMaterialFile != null) {
            if (mapMaterialFile.getType() == 2 && StringUtils.isNotBlank(mapMaterialFile.getPath())) {
                MinioUtil.removeFile(mapMaterialFile.getPath());
            }
        }
    }

    /**
     * @描述:排序列表
     * @入参:
     * @出参:
     * @作者: 戴伙进
     * @日期: 2020/6/4 15:22
     **/
    public static void sortList(List<MapMaterialFile> list, String parentId, List<MapMaterialFile> resultList, int level) {
        for (MapMaterialFile node : list) {
            if (node.getParentId().equals(parentId)) {
                node.setTreeLevel(level);
                resultList.add(node);
                sortList(list, node.getId(), resultList, level + 1);
            }
        }
    }

    /**
     * @描述:数据预处理
     * @入参:
     * @出参:
     * @作者: 戴伙进
     * @日期: 2020/6/3 9:56
     **/
    private void prepareData(MapMaterialFile mapMaterialFile, boolean isSave) {
        if (isSave) {
            mapMaterialFile.setCreator(getUser().getUserId().toString());
            mapMaterialFile.setCreateTime(new Date());
            //防止误修改
            mapMaterialFile.setModifier(null);
            mapMaterialFile.setModifyTime(null);
        } else {
            mapMaterialFile.setModifier(getUser().getUserId().toString());
            mapMaterialFile.setModifyTime(new Date());
            //防止误修改
            mapMaterialFile.setCreator(null);
            mapMaterialFile.setCreateTime(null);
        }
    }
}
